using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;

namespace Glpt.Data
{
    /// <summary>
    /// TXT文件分拆流类
    /// </summary>
    public class TXTDataReader : IDataReader
    {
        private StreamReader stream;
        private Dictionary<string, int> columnsByName = new Dictionary<string, int>();
        private Dictionary<int, string> columnsByOrdinal = new Dictionary<int, string>();
        private Dictionary<string, string> colnames_ext = new Dictionary<string, string>();//附加列
        private int Cols_ext = 0; //附加列的个数
        private int Col_rowIndex = -1;//自增字段列序号
        private string[] currentRow;
        private bool _isClosed = true;
        private int irowIndex = 0;
        private int ititleIndex = 0;
        private Encoding encoding = DataFuns.DefaultEncoding;
        private int COLCOUNT = 0;
        private string separator = ",";
        private int icol = -1;
        private int len = 0;
        private int len2 = 0;
        private char[] buffer = new char[10240];//每次读取到内存的字符数据
        private char[] buffer2 = new char[0];
        private int idx = 0;
        private int ibegin = 0;
        private StringBuilder sb = new StringBuilder();
        public Type[] FieldType
        {
            get;
            set;
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        public TXTDataReader()
        {
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="param">参数</param>
        public TXTDataReader(string fileName, Dictionary<string, string> param)
        {
            Init(fileName, param);
        }

        /// <summary>
        /// 参数设置
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="fieldterminator">分隔符</param>
        /// <param name="colcount">字段数量</param>
        /// <returns></returns>
        public TXTDataReader SetParams(string fileName, string fieldterminator, string colcount)
        {
            return SetParams(fileName, fieldterminator, colcount, "");
        }
        /// <summary>
        /// 参数设置
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="fieldterminator">分隔符</param>
        /// <param name="colcount">字段数量</param>
        /// <param name="titlerow">是否有标题</param>
        /// <returns></returns>
        public TXTDataReader SetParams(string fileName, string fieldterminator, string colcount, string titlerow)
        {
            return SetParams(fileName, fieldterminator, colcount, titlerow, "");
        }
        /// <summary>
        /// 参数设置
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="fieldterminator">分隔符</param>
        /// <param name="colcount">字段数量</param>
        /// <param name="titlerow">是否有标题</param>
        /// <returns></returns>
        public TXTDataReader SetParams(string fileName, string fieldterminator, string colcount, string titlerow, string codepage)
        {
            return SetParams(fileName, fieldterminator, colcount, titlerow, codepage, "");
        }
        /// <summary>
        /// 参数设置
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="fieldterminator">分隔符</param>
        /// <param name="colcount">字段数量</param>
        /// <param name="titlerow">是否有标题</param>
        /// <param name="codepage">编码</param>
        /// <param name="paramString">参数</param>
        /// <returns></returns>
        public TXTDataReader SetParams(string fileName, string fieldterminator, string colcount, string titlerow, string codepage, string paramString)
        {
            COLCOUNT = int.Parse(colcount);
            if (separator.Length > 0) SetFieldterminator(fieldterminator);
            if (titlerow.Length > 0) int.TryParse(titlerow, out ititleIndex);
            if (codepage.Length > 0) encoding = Encoding.GetEncoding(codepage);
            Dictionary<string, string> param = null;
            if (paramString.Length > 0) param = DataFuns.SplitStringValue(paramString, ',');
            Init(fileName, param);
            return this;
        }

        private void SetFieldterminator(string fieldterminator)
        {
            separator = DataFuns.ConvertFromHexString(fieldterminator);
        }

        private void Init(string fileName, Dictionary<string, string> param)
        {
            if (param != null)
            {
                if (param.ContainsKey("COLCOUNT") && param["COLCOUNT"].Length > 0)
                    COLCOUNT = int.Parse(param["COLCOUNT"]);
                if (param.ContainsKey("FIELDTERMINATOR") && param["FIELDTERMINATOR"].Length > 0)
                    SetFieldterminator(param["FIELDTERMINATOR"]);
                if (param.ContainsKey("CODEPAGE") && param["CODEPAGE"].Length > 0)
                    encoding = Encoding.GetEncoding(param["CODEPAGE"]);
                if (param.ContainsKey("TITLEROW") && param["TITLEROW"].Length > 0)
                {
                    int.TryParse(param["TITLEROW"], out ititleIndex);
                }
                foreach (string key in param.Keys)
                {
                    if (key.StartsWith(":"))
                    {
                        colnames_ext.Add(key.Substring(1), param[key]);
                    }
                }
            }
            if (COLCOUNT < 1)
                throw new Exception("没有设置COLCOUNT参数");
            if (separator.Length < 1)
                throw new Exception("没有设置FIELDTERMINATOR参数");
            this.stream = new StreamReader(fileName, encoding);
            Cols_ext = 0;
            foreach (string key in colnames_ext.Keys)
            {
                columnsByName.Add(key, Cols_ext);
                columnsByOrdinal.Add(Cols_ext, key);
                if (colnames_ext[key].Equals("IDENTITY")) Col_rowIndex = Cols_ext;
                Cols_ext++;
            }
            bool is_init = false;
            if (ititleIndex > 0)//有标题
            {
                if (Read())
                {
                    for (int i = 0; i < currentRow.Length; i++)
                    {
                        columnsByName.Add(currentRow[i], i + Cols_ext);
                        columnsByOrdinal.Add(i + Cols_ext, currentRow[i]);
                    }
                    is_init = true;
                }
            }
            if (!is_init)
            {
                for (int i = 0; i < COLCOUNT; i++)
                {
                    string colname = "COL" + (i + 1).ToString();
                    columnsByName.Add(colname, i + Cols_ext);
                    columnsByOrdinal.Add(i + Cols_ext, colname);
                }
            }
            _isClosed = false;
        }

        public bool Read()
        {
            if (stream == null) return false;
            bool has_data = false;
            if (!stream.EndOfStream || idx < len)
            {
                currentRow = new string[COLCOUNT];
                irowIndex++;
                while (!stream.EndOfStream || idx < len)
                {
                    if (idx >= len)
                    {
                        len = stream.ReadBlock(buffer, 0, buffer.Length);
                        len2 = 0;
                        ibegin = 0;
                        idx = 0;
                    }
                    for (; idx < len; idx++)
                    {
                        if (buffer[idx] == separator[0])
                        {
                            bool all_same = true;
                            for (int j = 1; j < separator.Length; j++)
                            {
                                if (idx + j < buffer.Length)
                                {
                                    if (buffer[idx + j] != separator[j])
                                    {
                                        all_same = false;
                                        break;
                                    }
                                }
                                else if (len2 == 0)
                                {
                                    int rdcount = idx + separator.Length - buffer.Length;//还差多少个字符
                                    buffer2 = new char[rdcount];
                                    if (!stream.EndOfStream)
                                    {
                                        len2 = stream.ReadBlock(buffer2, 0, rdcount);
                                        j--;
                                        continue;
                                    }
                                    else
                                    {
                                        all_same = false;
                                        break;
                                    }
                                }
                                else
                                {
                                    if (buffer2[idx + j - buffer.Length] != separator[j])
                                    {
                                        all_same = false;
                                        break;
                                    }
                                }
                            }
                            if (all_same)
                            {
                                icol++;
                                sb.Append(buffer, ibegin, idx - ibegin);
                                currentRow[icol] = sb.ToString();
                                sb.Clear();
                                ibegin = idx + separator.Length;
                                idx = ibegin;
                                if (icol == COLCOUNT - 1)
                                {
                                    icol = -1;
                                    has_data = true;
                                    break;
                                }
                            }
                            else if (len2 > 0)
                            {
                                sb.Append(buffer, ibegin, len - ibegin);
                                sb.Append(buffer2, 0, len2);
                                ibegin = len;
                            }
                            len2 = 0;
                        }
                    }
                    if (has_data)
                        break;
                    else if (len > ibegin)
                    {
                        sb.Append(buffer, ibegin, len - ibegin);
                    }
                }
                if (!has_data && icol < COLCOUNT - 1)
                {
                    icol++;
                    currentRow[icol] = sb.ToString();
                    sb.Clear();
                    has_data = true;
                }
            }
            return has_data;
        }

        /// <summary>
        /// 关闭StreamReader对象和基础流
        /// </summary>
        public void Close()
        {
            if (stream != null) stream.Close();
            _isClosed = true;
        }

        public int FieldCount
        {
            get { return columnsByName.Count; }
        }
        public string GetString(int i)
        {
            if (i == Col_rowIndex)
                return irowIndex.ToString();
            else if (i < Cols_ext)
                return colnames_ext[columnsByOrdinal[i]];
            else
                return currentRow[i - Cols_ext];
        }

        public object GetValue(int i)
        {
            if (i == Col_rowIndex)
                return irowIndex;
            else if (i < Cols_ext)
            {
                if (FieldType == null)
                    return colnames_ext[columnsByOrdinal[i]];
                else
                    return DataFuns.ConvertValue(colnames_ext[columnsByOrdinal[i]], FieldType[i]);
            }
            else
            {
                if (i - Cols_ext >= currentRow.Length)
                    throw new Exception("第" + irowIndex.ToString() + "行的列数太少");
                if (FieldType == null)
                {
                    if (currentRow[i - Cols_ext].Length > 0)
                        return currentRow[i - Cols_ext];
                    else
                        return DBNull.Value;
                }
                else
                    return DataFuns.ConvertValue(currentRow[i - Cols_ext], FieldType[i]);
            }
        }

        public string GetName(int i)
        {
            return columnsByOrdinal[i];
        }

        public int GetOrdinal(string name)
        {
            return columnsByName[name];
        }

        public int Depth
        {
            get
            {
                return 0;
            }
        }

        public bool IsClosed
        {
            get
            {
                return _isClosed;
            }
        }

        public int RecordsAffected
        {
            get
            {
                return -1;
            }
        }

        public DataTable GetSchemaTable()
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("ColumnName", typeof(string));
            dt.Columns.Add("ColumnOrdinal", typeof(int));
            dt.Columns.Add("ColumnSize", typeof(int)).DefaultValue = 255;
            dt.Columns.Add("NumericPrecision", typeof(short)).DefaultValue = 255;
            dt.Columns.Add("NumericScale", typeof(short)).DefaultValue = 255;
            dt.Columns.Add("IsUnique", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsKey", typeof(bool));
            dt.Columns.Add("BaseServerName", typeof(string));
            dt.Columns.Add("BaseCatalogName", typeof(string));
            dt.Columns.Add("BaseColumnName", typeof(string));
            dt.Columns.Add("BaseSchemaName", typeof(string));
            dt.Columns.Add("BaseTableName", typeof(string));
            dt.Columns.Add("DataType", typeof(System.Type));
            dt.Columns.Add("AllowDBNull", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("ProviderType", typeof(int)).DefaultValue = 22;
            dt.Columns.Add("IsAliased", typeof(bool));
            dt.Columns.Add("IsExpression", typeof(bool));
            dt.Columns.Add("IsIdentity", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsAutoIncrement", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsRowVersion", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsHidden", typeof(bool));
            dt.Columns.Add("IsLong", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("IsReadOnly", typeof(bool)).DefaultValue = false;
            dt.Columns.Add("ProviderSpecificDataType", typeof(System.Type));
            dt.Columns.Add("XmlSchemaCollectionDatabase", typeof(string));
            dt.Columns.Add("XmlSchemaCollectionOwningSchema", typeof(string));
            dt.Columns.Add("XmlSchemaCollectionName", typeof(string));
            dt.Columns.Add("UdtAssemblyQualifiedName", typeof(string)).DefaultValue = 22;
            dt.Columns.Add("NonVersionedProviderType", typeof(int));
            dt.Columns.Add("IsColumnSet", typeof(bool)).DefaultValue = false;
            for (int i = 0; i < FieldCount; i++)
            {
                DataRow dr = dt.NewRow();
                dr["ColumnName"] = GetName(i);
                dr["ColumnOrdinal"] = i;
                dr["BaseColumnName"] = GetName(i);
                dr["DataType"] = GetFieldType(i);
                dr["ProviderSpecificDataType"] = GetFieldType(i);
                dt.Rows.Add(dr);
            }
            return dt;
        }

        public bool NextResult()
        {
            return false;
        }

        public object this[int i]
        {
            get
            {
                return GetValue(i);
            }
        }
        public object this[string name]
        {
            get
            {
                return GetValue(columnsByName[name]);
            }
        }
        public bool GetBoolean(int i)
        {
            return bool.Parse(GetString(i));
        }
        public byte GetByte(int i)
        {
            return byte.Parse(GetString(i));
        }
        public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
        {
            return -1;
        }
        public char GetChar(int i)
        {
            return char.Parse(GetString(i));
        }
        public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
        {
            return -1;
        }
        public IDataReader GetData(int i)
        {
            return null;
        }
        public string GetDataTypeName(int i)
        {
            return GetFieldType(i).Name;
        }
        public DateTime GetDateTime(int i)
        {
            return DateTime.Parse(GetString(i));
        }
        public decimal GetDecimal(int i)
        {
            return decimal.Parse(GetString(i));
        }
        public double GetDouble(int i)
        {
            return double.Parse(GetString(i));
        }
        public Type GetFieldType(int i)
        {
            if (FieldType == null)
                return typeof(string);
            else
                return FieldType[i];
        }
        public float GetFloat(int i)
        {
            return float.Parse(GetString(i));
        }
        public Guid GetGuid(int i)
        {
            return Guid.Parse(GetString(i));
        }
        public short GetInt16(int i)
        {
            return short.Parse(GetString(i));
        }
        public int GetInt32(int i)
        {
            return int.Parse(GetString(i));
        }
        public long GetInt64(int i)
        {
            return long.Parse(GetString(i));
        }
        public int GetValues(object[] values)
        {
            for (int i = 0; i < columnsByName.Count; i++)
                values[i] = GetValue(i);
            return columnsByName.Count;
        }
        public bool IsDBNull(int i)
        {
            return (GetValue(i) == DBNull.Value);
        }
        public void Dispose()
        {
            try
            {
                GC.Collect();
            }
            catch { }
        }
        ~TXTDataReader()
        {
            Dispose();
        }
    }
}
